package com.cxx.food.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cxx.food.constant.BizType;
import com.cxx.food.dao.FoodDao;
import com.cxx.food.dto.*;
import com.cxx.food.entity.*;
import com.cxx.food.entity.Record;
import com.cxx.food.mapper.*;
import com.cxx.food.service.CommentService;
import com.cxx.food.service.FavouriteService;
import com.cxx.food.service.FoodService;
import com.cxx.food.service.LikeService;
import com.cxx.food.vo.FoodQueryVo;
import com.cxx.framework.web.CustomException;
import com.github.yitter.idgen.YitIdHelper;
import jakarta.annotation.Resource;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

@Service
public class FoodServiceImpl implements FoodService {
    @Resource
    private FoodDao foodDao;

    @Resource
    private RecipeMapper recipeMapper;

    @Resource
    private MaterialMapper materialMapper;

    @Resource
    private StepMapper stepMapper;

    @Resource
    private RecordMapper recordMapper;

    @Resource
    private LikeService likeService;

    @Resource
    private CommentService commentService;

    @Resource
    private FavouriteService favouriteService;

    @Override
    @Transactional
    public Boolean addRecipe(RecipeDto recipeDto) {
        LambdaQueryWrapper<Recipe> queryWrapper = new LambdaQueryWrapper<>();
        if (recipeMapper.exists(queryWrapper.eq(Recipe::getName, recipeDto.getName())
                .eq(Recipe::getCreateBy, StpUtil.getLoginIdAsString()))) {
            throw new CustomException("该菜谱已经存在");
        }

        Recipe recipe = new Recipe();
        recipe.setIsShare(recipeDto.getIsShare() ? 1 : 0);
        BeanUtils.copyProperties(recipeDto, recipe);
        recipeMapper.insert(recipe);
        resetFoodRecipe(recipe.getId(), recipeDto);

        return Boolean.TRUE;
    }

    @Override
    @Transactional
    public Boolean updateRecipe(Long id, RecipeDto recipeDto) {
        if (recipeMapper.selectById(id) == null) {
            throw new CustomException("该菜谱不存在");
        }

        Recipe recipe = new Recipe();
        recipe.setIsShare(recipeDto.getIsShare() ? 1 : 0);
        BeanUtils.copyProperties(recipeDto, recipe);
        recipe.setId(id);
        recipeMapper.updateById(recipe);
        resetFoodRecipe(recipe.getId(), recipeDto);

        return Boolean.TRUE;
    }

    @Override
    @Transactional
    public Boolean deleteRecipe(Long id) {
        recipeMapper.deleteById(id);
        materialMapper.delete(Wrappers.lambdaQuery(Material.class).eq(Material::getFoodId, id));
        stepMapper.delete(Wrappers.lambdaQuery(Step.class).eq(Step::getFoodId, id));
        recordMapper.delete(Wrappers.lambdaQuery(Record.class).eq(Record::getFoodId, id));

        return Boolean.TRUE;
    }

    @Override
    public Boolean addRecord(RecordDto recordDto) {
        LambdaQueryWrapper<Recipe> queryWrapper = new LambdaQueryWrapper<>();
        Recipe recipe = recipeMapper.selectOne(queryWrapper.eq(Recipe::getName, recordDto.getName())
                .eq(Recipe::getCreateBy, StpUtil.getLoginIdAsString()));
        long recipeId;
        if (recipe == null) {
            Recipe newRecipe = new Recipe();
            newRecipe.setName(recordDto.getName());
            newRecipe.setCategory("其他");
            newRecipe.setRecommendRate(0);
            newRecipe.setIsShare(0);
            newRecipe.setRemark("");
            recipeMapper.insert(newRecipe);
            recipeId = newRecipe.getId();
        } else {
            recipeId = recipe.getId();
        }

        Record record = new Record();
        record.setFoodId(recipeId);
        record.setPerson(StpUtil.getLoginIdAsString());
        BeanUtils.copyProperties(recordDto, record);
        recordMapper.insert(record);

        return Boolean.TRUE;
    }

    @Override
    public Boolean updateRecord(Long id, RecordDto recordDto) {
        if (recordMapper.selectById(id) == null) {
            throw new CustomException("该记录不存在");
        }

        Record record = new Record();
        BeanUtils.copyProperties(recordDto, record);
        record.setId(id);
        recordMapper.updateById(record);

        return Boolean.TRUE;
    }

    @Override
    public Boolean deleteRecord(Long id) {
        recordMapper.deleteById(id);

        return Boolean.TRUE;
    }

    @Override
    public Boolean addComment(Long recipeId, String content) {
        if (recipeMapper.selectById(recipeId) == null) {
            throw new CustomException("该菜谱不存在");
        }

        return commentService.addComment(BizType.RECIPE, recipeId, content);
    }

    @Override
    public Boolean addLike(Long recipeId) {
        if (recipeMapper.selectById(recipeId) == null) {
            throw new CustomException("该菜谱不存在");
        }

        return likeService.addLike(BizType.RECIPE, recipeId);
    }

    @Override
    public Boolean deleteLike(Long recipeId) {
        return likeService.deleteLike(recipeId);
    }

    @Override
    public Boolean addFavourite(Long recipeId) {
        if (recipeMapper.selectById(recipeId) == null) {
            throw new CustomException("该菜谱不存在");
        }

        return favouriteService.addFavourite(BizType.RECIPE, recipeId);
    }

    @Override
    public Boolean deleteFavourite(Long recipeId) {
        return favouriteService.deleteFavourite(recipeId);
    }

    private void resetFoodRecipe(Long id, RecipeDto recipeDto) {
        materialMapper.delete(Wrappers.lambdaQuery(Material.class).eq(Material::getFoodId, id));
        stepMapper.delete(Wrappers.lambdaQuery(Step.class).eq(Step::getFoodId, id));

        insertMaterial(id, recipeDto.getMaterialList());
        insertStep(id, recipeDto.getStepList());
    }

    private void insertMaterial(Long foodId, List<MaterialDto> materialDtoList) {
        if (materialDtoList.isEmpty()) {
            return;
        }

        materialDtoList.forEach((item) -> {
            item.setId(YitIdHelper.nextId());
        });
        foodDao.insertMaterial(foodId, materialDtoList);
    }

    private void insertStep(Long foodId, List<StepDto> stepDtoList) {
        if (stepDtoList.isEmpty()) {
            return;
        }

        stepDtoList.forEach((item) -> {
            item.setId(YitIdHelper.nextId());
        });
        foodDao.insertStep(foodId, stepDtoList);
    }

    @Override
    public RecipeDto queryRecipeById(Long id) {
        if (recipeMapper.selectById(id) == null) {
            throw new CustomException("该美食不存在");
        }

        return foodDao.queryRecipeById(id);
    }

    @Override
    public List<SummaryDto> queryRecipeByUser(Long id) {
        return foodDao.queryRecipeByUser(id);
    }

    @Override
    public List<SummaryDto> queryRecipeUserFavourite() {
        return foodDao.queryRecipeUserFavourite(StpUtil.getLoginIdAsString());
    }

    @Override
    public List<SummaryDto> queryRecipeSummary(FoodQueryVo foodQueryVo) {
        return foodDao.queryRecipeSummary(foodQueryVo, StpUtil.getLoginIdAsString());
    }

    @Override
    public IPage<SummaryDto> querySummaryByPage(Integer currentPage, Integer pageSize, FoodQueryVo foodQueryVo) {
        // 这里只能先对主表进行分页 然后在查询子表信息
        // 不能使用Left Join关联表查询，因为这里是一对多数据，关联查询会导致数据有重复
        // 从而导致page计算错误（会把重复的数据当成多条记录，实际上只有1条记录）
        IPage<SummaryDto> summaryPage = foodDao.querySummaryByPage(new Page<>(currentPage, pageSize), foodQueryVo);
        List<SummaryDto> summaryList = summaryPage.getRecords();
        for (SummaryDto foodSummary : summaryList) {
            foodSummary.setRecordList(foodDao.queryRecordById(foodSummary.getId()));
        }

        summaryPage.setRecords(summaryList);
        return summaryPage;
    }

    @Override
    public List<RecordDto> queryRecord(String startDate, String endDate) {
        return foodDao.queryRecord(startDate, endDate, StpUtil.getLoginIdAsString());
    }

    @Override
    public List<String> queryFoodName() {
        LambdaQueryWrapper<Recipe> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Recipe::getCreateBy, StpUtil.getLoginIdAsString());
        return recipeMapper.selectList(queryWrapper)
                .stream().map(Recipe::getName).collect(Collectors.toList());
    }

    @Override
    public List<String> queryCategory() {
        return foodDao.queryCategory(StpUtil.getLoginIdAsString());
    }

    @Override
    public StatsDto queryStats() {
        return foodDao.queryStats(StpUtil.getLoginIdAsString());
    }

    @Override
    public List<StatsChartDto> queryRecordStats() {
        return foodDao.queryRecordStats(StpUtil.getLoginIdAsString());
    }

    @Override
    public List<StatsChartDto> queryCategoryStats() {
        return foodDao.queryCategoryStats(StpUtil.getLoginIdAsString());
    }

    @Override
    public List<StatsChartDto> queryRankStats() {
        return foodDao.queryRankStats(StpUtil.getLoginIdAsString());
    }
}
